package nachos.userprog;

import nachos.machine.*;
import nachos.threads.*;

import java.util.LinkedList;

/**
 * 可以支持多个用户进程的内核
 */
public class UserKernel extends ThreadedKernel {
    /**
     * 分配一个新的用户内核
     */
    public UserKernel() {
        super();

        // 初始化内存列表
        AllFreePageNums = new LinkedList<>();

        // 获取物理页数
        int numPhysPages = Machine.processor().getNumPhysPages();

        // 为空闲页编号
        for (int i = 0; i < numPhysPages; i++) {
            AllFreePageNums.add(i);
        }
    }

    /**
     * 初始化这个内核
     * 创建同步控制台并设置处理器的异常处理程序
     */
    public void initialize(String[] args) {
        super.initialize(args);

        console = new SynchConsole(Machine.console());

        Machine.processor().setExceptionHandler(new Runnable() {
            public void run() {
                exceptionHandler();
            }
        });
    }

    /**
     * 测试控制台设备
     */
    public void selfTest() {
        super.selfTest();

//        System.out.println("Testing the console device. Typed characters");
//        System.out.println("will be echoed until q is typed.");
//
//        char c;
//
//        do {
//            c = (char) console.readByte(true);
//            console.writeByte(c);
//        }
//        while (c != 'q');
//
//        System.out.println("");
    }

    /**
     * 返回当前进程
     *
     * @return 当前进程，如果当前没有进程则为 null
     */
    public static UserProcess currentProcess() {
        if (!(KThread.currentThread() instanceof UThread))
            return null;

        return ((UThread) KThread.currentThread()).process;
    }

    /**
     * 异常处理程序
     * 每当用户指令导致处理器异常时，处理器就会调用此处理程序
     * <p>
     * 调用异常处理程序时，会启用中断，处理器的原因寄存器包含一个整数，用于标识异常的原因
     * （请参阅 Processor 类中的 exceptionZZZ 常量）
     * 如果异常涉及错误的虚拟地址，例如页面错误、TLB未命中、只读、总线错误或地址错误，
     * 那么处理器的BadVAddr寄存器会识别导致异常的虚拟地址
     */
    public void exceptionHandler() {
        Lib.assertTrue(KThread.currentThread() instanceof UThread);

        UserProcess process = ((UThread) KThread.currentThread()).process;
        int cause = Machine.processor().readRegister(Processor.regCause);
        process.handleException(cause);
    }

    /**
     * 通过创建进程并在其中运行 shell 程序，开始运行用户程序
     * 必须运行的 shell 程序的名称由 Machine.getShellProgramName() 返回
     *
     * @see nachos.machine.Machine#getShellProgramName
     */
    public void run() {
        super.run();

        // 创建一个用户进程
        UserProcess process = UserProcess.newUserProcess();

        // 在该用户进程执行 shell 用户程序
        String shellProgram = Machine.getShellProgramName();
        Lib.assertTrue(process.execute(shellProgram, new String[]{}));

        KThread.currentThread().finish();
    }

    /**
     * 终止此内核，永远不会返回
     */
    public void terminate() {
        super.terminate();
    }

    /**
     * 对同步控制台的全局可访问引用
     */
    public static SynchConsole console;

    // dummy variables to make javac smarter
    private static Coff dummy1 = null;

    // 空闲页列表
    private static LinkedList<Integer> AllFreePageNums;

    // 返回给定数量的空闲页号，如果空闲页不足，则返回空链表
    public static LinkedList<Integer> getFreePageNums(int numPages) {
        // 声明并初始化一个空闲页号链表
        LinkedList<Integer> freePageNums = new LinkedList<>();

        // 如果空闲页足够
        if (AllFreePageNums.size() >= numPages) {
            // 从空闲页中取出指定数量的页号，并添加到 freePages 中
            for (int i = 0; i < numPages; i++) {
                freePageNums.add(AllFreePageNums.removeFirst());
            }
        }

        return freePageNums;
    }

    // 归还空闲页
    public static void releaseOwnPageNums(LinkedList<Integer> ownPageNums){
        // 如果进程没有占有页，直接返回
        if (ownPageNums == null || ownPageNums.isEmpty()) {
            return;
        }

        // 将进程中页号转换成空闲页号
        for (int i = 0; i < ownPageNums.size(); i ++) {
            AllFreePageNums.add(ownPageNums.removeFirst());
        }
    }
}
